import ScrollViewItemBase from "./ScrollViewItemBase";
import Util from "./Util";

const { ccclass, property } = cc._decorator;

@ccclass
export default class LoopScrollView extends cc.Component {
    @property({ tooltip: "循环列表中的单项预制" } || cc.Boolean)
    @property({ type: cc.Prefab, serializable: true, displayName: "单项预制" })
    itemPrefab: cc.Prefab = null;

    @property({ tooltip: "循环列表" } || cc.Boolean)
    @property({ type: cc.ScrollView, serializable: true, displayName: "循环列表" })
    scrollView: cc.ScrollView = null;

    @property({ tooltip: "单项脚本名称" } || cc.Boolean)
    @property({ serializable: true, displayName: "单项脚本名称(string)" })
    itemScriptName = "ScrollViewItemBase";

    @property({ tooltip: "spawnCount" } || cc.Boolean)
    @property({ type: cc.Float, serializable: true, displayName: "实际创建的项数量" })
    spawnCount = 0.0;

    @property({ tooltip: "totalCount" } || cc.Boolean)
    @property({ type: cc.Float, serializable: true, displayName: "在列表中显示的项数量" })
    totalCount = 0.0;

    @property({ tooltip: "项之间的间隔大小" } || cc.Boolean)
    @property({ type: cc.Float, serializable: true, displayName: "项之间的间隔大小" })
    spacing = 5.0;

    item: cc.Node = null;
    bufferZone = 0;
    lastContentPosY = 0;
    mContentPosY_Original = 0;
    content: cc.Node = null;
    items: cc.Node[] = [];// 存储实际创建的项数组

    /**>1代表是多行多列循环列表 */
    mChildNums = 1;

    onLoad() {
        if (this.itemScriptName == "") {
            this.itemScriptName = "ScrollViewItemBase";
        }
        this.content = this.scrollView.content;
        this.initialize();
        // 使用这个变量来判断滚动操作是向上还是向下
        this.lastContentPosY = 0;
        // 设定缓冲矩形的大小为实际创建项的高度累加，当某项超出缓冲矩形时，则更新该项的显示内容
        this.bufferZone = this.spawnCount * (this.item.height + this.spacing) / 2;

        this.scrollView.node.on('scrolling', () => {
            let items = this.items;
            // 如果当前content的y坐标小于上次记录值，则代表往下滚动，否则往上。
            let isDown = this.scrollView.content.y < this.lastContentPosY;
            // 实际创建项占了多高（即它们的高度累加）
            let offset = (this.item.height + this.spacing) * items.length;
            let newY = 0;

            // 遍历数组，更新item的位置和显示
            for (let i = 0; i < items.length; ++i) {
                let viewPos = this.getPositionInView(items[i]);
                if (isDown) {
                    // 提前计算出该item的新的y坐标
                    newY = items[i].y + offset;
                    // 如果往下滚动时item已经超出缓冲矩形，且newY未超出content上边界，
                    // 则更新item的坐标（即上移了一个offset的位置），同时更新item的显示内容
                    if (viewPos.y < -this.bufferZone && newY < 0) {
                        items[i].y = newY;
                        let item: ScrollViewItemBase = items[i].getComponent(this.itemScriptName);
                        let itemId = item.itemID - items.length; // update item id
                        item.updateItem(itemId, itemId);
                    }
                } else {
                    // 提前计算出该item的新的y坐标
                    newY = items[i].y - offset;
                    // 如果往上滚动时item已经超出缓冲矩形，且newY未超出content下边界，
                    // 则更新item的坐标（即下移了一个offset的位置），同时更新item的显示内容
                    if (viewPos.y > this.bufferZone && newY > -this.content.height) {
                        items[i].y = newY;
                        let item: ScrollViewItemBase = items[i].getComponent(this.itemScriptName);
                        let itemId = item.itemID + items.length;
                        item.updateItem(itemId, itemId);
                    }
                }
            }

            // 更新lastContentPosY和总项数显示
            this.lastContentPosY = this.scrollView.content.y;
        }, this);
    };

    private initialize() {
        if (this.items.length > 0) {
            return;
        }
        if (this.content == null) {
            this.content = this.scrollView.content;
        }
        this.mContentPosY_Original = this.content.y;
        // 获取整个列表的高度
        this.item = Util.instance().pool(this.itemPrefab);
        let scriptName: ScrollViewItemBase = this.item.getComponent(this.itemScriptName);
        this.mChildNums = scriptName.mChildNums;
        Util.instance().put(this.item);
        let layoutNums = Math.ceil(this.totalCount / this.mChildNums);
        this.content.height = layoutNums * (this.item.height + this.spacing) + this.spacing;
        for (let i = 0; i < this.spawnCount; ++i) { // spawn items, we only need to do this once
            let item = Util.instance().pool(this.itemPrefab);
            this.content.addChild(item);
            // 设置该item的坐标（注意父节点content的Anchor坐标是(0.5, 1)，所以item的y坐标总是负值）
            item.setPosition(0, -item.height * (0.5 + i) - this.spacing * (i + 1));
            let scriptName: ScrollViewItemBase = item.getComponent(this.itemScriptName);
            scriptName.updateItem(i, i);
            item.active = false;
            this.items.push(item);
        }
    };

    // 返回item在ScrollView空间的坐标值
    getPositionInView(item) {
        let worldPos = item.parent.convertToWorldSpaceAR(item.position);
        let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
        return viewPos;
    };

    addItem(addNums) {
        this.initialize();
        let newTotalCount = Math.max(0, this.totalCount + addNums);
        let oldLayoutNums = Math.ceil(this.totalCount / this.mChildNums);
        let newLayoutNums = Math.ceil(newTotalCount / this.mChildNums);
        let addNums_Show = Math.min(newLayoutNums, this.spawnCount);
        if (oldLayoutNums < this.spawnCount) {
            //显示小于实际创建spawnCount
            for (let i = oldLayoutNums; i < addNums_Show; i++) {
                let item = this.items[i];
                item.active = true;
                let itemScrip: ScrollViewItemBase = item.getComponent(this.itemScriptName);
                let itemId = i;
                itemScrip.updateItem(itemId, itemId);
            }
        }
        //多行多列防止正好为同一行添加
        if (this.mChildNums > 1) {
            if (newLayoutNums <= this.spawnCount) {
                let item = this.items[newLayoutNums - 1];
                item.active = true;
                let itemScrip: ScrollViewItemBase = item.getComponent(this.itemScriptName);
                let itemId = newLayoutNums - 1;
                itemScrip.updateItem(itemId, itemId);
            } else {
                let layoutIndex = newLayoutNums % this.mChildNums - 1;
                layoutIndex = layoutIndex < 0 ? this.spawnCount - 1 : layoutIndex;
                let item = this.items[layoutIndex];
                let maxYItem = this.getItemAtBottom();
                if (maxYItem == item) {
                    //当前子物体全部激活状态则不需要更新
                    let childNodeActiveNums = this.getItemChildActiveNums(item);
                    if (childNodeActiveNums >= this.mChildNums) {
                        return;
                    }
                    let itemScrip: ScrollViewItemBase = item.getComponent(this.itemScriptName);
                    let itemId = newLayoutNums - 1;
                    itemScrip.updateItem(itemId, itemId);
                }
            }
        }
        this.content.height = (newLayoutNums) * (this.item.height + this.spacing) + this.spacing; // get total content height
        this.totalCount = newTotalCount;
    };

    removeItem(removeNums) {
        this.initialize();
        if (this.totalCount == 0) {
            return;
        }
        let oldLayoutNums = Math.ceil(this.totalCount / this.mChildNums);
        let newTotalCount = Math.max(0, this.totalCount - removeNums);
        let newLayoutNums = Math.ceil(newTotalCount / this.mChildNums);
        if (newLayoutNums < this.spawnCount) {
            //隐藏小于实际创建spawnCount
            for (let i = newLayoutNums; i < this.spawnCount; i++) {
                let item = this.items[i];
                item.active = false;
            }
        }
        //多行多列删除
        if (this.mChildNums > 1) {
            let layoutIndex = oldLayoutNums % this.mChildNums - 1;
            layoutIndex = layoutIndex < 0 ? this.spawnCount - 1 : layoutIndex;
            let item = this.items[layoutIndex];
            let maxYItem = this.getItemAtBottom();
            if (maxYItem == item || maxYItem.active == false) {
                if (newLayoutNums < oldLayoutNums) {
                    item.active = false;
                } else {
                    let itemScrip: ScrollViewItemBase = item.getComponent(this.itemScriptName);
                    let itemId = oldLayoutNums - 1;
                    itemScrip.updateItem(itemId, itemId);
                }
            }
        }

        this.content.height = (newLayoutNums) * (this.item.height + this.spacing) + this.spacing; // get total content height
        this.totalCount = newTotalCount;
        this.moveBottomItemToTop();
        this.RefreshItemDB();
    };

    moveBottomItemToTop() {
        let offset = (this.item.height + this.spacing) * this.items.length;
        let length = this.items.length;
        let item = this.getItemAtBottom();

        // whether need to move to top
        if (item.y + offset < 0 && (this.getItemChildActiveNums(item) <= 0 || item.active == false)) {
            let itemComp: ScrollViewItemBase = item.getComponent(this.itemScriptName);
            //防止单行单列中间移除露馅
            if (this.mChildNums <= 1 && itemComp.itemID < this.totalCount) {
                return;
            }
            let itemId = itemComp.itemID - length;
            itemComp.updateItem(itemId, itemId);
            item.y = item.y + offset;
        }
    }

    getItemChildActiveNums(item: cc.Node) {
        let childNodeActiveNums = 0;
        let itemScrip: ScrollViewItemBase = item.getComponent(this.itemScriptName);
        for (let i = 0; i < itemScrip.mChildNode.length; i++) {
            let childNode = itemScrip.mChildNode[i];
            if (childNode == null) {
                cc.error("需要在单项onLoad赋值子物体");
                return;
            }
            if (childNode.active == true) {
                childNodeActiveNums += 1;
            }
        }
        return childNodeActiveNums;
    }

    getItemAtBottom() {
        let item = this.items[0];
        for (let i = 1; i < this.items.length; ++i) {
            if (item.y > this.items[i].y) {
                item = this.items[i];
            }
        }
        return item;
    }

    /**索引排序后刷数据层 */
    private RefreshItemDB() {
        this.items.forEach(item => {
            if (item.active) {
                let itemScrip: ScrollViewItemBase = item.getComponent(this.itemScriptName);
                itemScrip.updateItem(itemScrip.itemID, itemScrip.itemID);
            }
        });
    }

    /**数据层改变刷新totalCount与UI */
    RefreshItem(newTotalCount: number) {
        if (this.totalCount == newTotalCount) {
            this.RefreshItemDB();
            return;
        }
        if (this.totalCount < newTotalCount) {
            let addNums = newTotalCount - this.totalCount;
            this.addItem(addNums);
        } else {
            newTotalCount = Math.max(0, newTotalCount);
            let removeNums = this.totalCount - newTotalCount;
            this.removeItem(removeNums);
        }
    }

    /**一定时间内,滚动到指定位置 offset相对于整个容器百分比值*/
    scrollToFixedPosition(offset: number, time: number) {
        this.scrollView.scrollToOffset(cc.p(0, offset), time);
    };
}